#ifndef GUSER_H
#define GUSER_H

#include<QThread>
#include<QObject>
#include<QByteArray>
#include<QTcpSocket>
#include<QFile>
#include<QTextStream>
#include<QString>
#include<QHostAddress>
#include<QDateTime>
#include<time.h>
#include<QStringList>

#define DEBUG 1

#include<iostream>

using namespace std;

#include"GFriendsList.h"
#include"GSquelchList.h"
#include"GSupport.h"

#define FRIENDFILE(u) (tr("USERS/") + u + tr("/friend.conf"))
#define SQUELCHFILE(u) (tr("USERS/") + u + tr("/squelch.conf"))


/*!	\brief This class handles message passing between threads.
 *	
 * This class associates signals with a username so this
 *	thread may send messages to other threads.
*/
class GChatMap : public QObject {
	Q_OBJECT
public:
	/*!	The name of the user this instance allows you to talk to.
	*/
	QString na;
	
	void sendTheMessage(QString user, QString msg) {
		emit sendMessage(user, msg);
	}
	
	void closeTheMessage(QString user) {
		emit closeMessage(user);
	}
	
signals:
	void sendMessage(QString user, QString msg);
	void closeMessage(QString user);
	
public slots:
	
};

/*!	\brief This class handles file transfer.
 *	
 *	This class associates signals with a username so this
 *	thread may send messages to other threads.
*/
class GFileMap : public QObject {
	Q_OBJECT
public:
	QString na;
	QString fn;
	bool approved;
	
	GFileMap() {
		approved = false;
	}
	
	void sendTheChunk(QString user, QString chunk) {
		emit sendChunk(user, fn, chunk);
	}
	
	void requestTheTransfer(QString user, QString fn, int size) {
		emit requestTransfer(user, fn, size);
	}
	
	void closeTheTransfer(QString user, QString fn) {
		emit closeTheTransfer(user, fn);
	}
	
signals:
	void sendChunk(QString name, QString fn, QString chunk);
	void requestTransfer(QString user, QString fn, int size);
	void closeTransfer(QString user, QString fn);
	
public slots:
	
};




/*!	\brief Thread that handles incoming connections.
 *  
 *  After a connection is established this class handles all incoming connections.
 *  
 *  
 *  
 *  
 *  
*/
class GUser : public QThread {
	Q_OBJECT
public:
	/*!	\brief The constructor for the this object.
	 *	
	 *	@param parent The parent of this instance.
	 *	@param sockfd The socket file descriptor to initialize this object with
	 *		so it may talk to the newly connected user.
	 *	
	*/
	GUser(QObject* parent, int sockfd);
	
	/*! \brief The destructor closes the connection and handles logout.
	*/
	~GUser();
	
	QString userName();
	
protected:
	/*!	\brief This is the name of the user which is set after logging in.
	*/
	QString name;
	
	/*!	\brief Stores the list of friends set by this user.
	*/
	GFriendsList flist;
	
	/*!	\brief Stores the list of people squelched by this user.
	*/
	GSquelchList slist;
	
	
	//buffer, stream and function for sending data
	/*!	\brief The actual socket that is used for the networking.
	*/
	QTcpSocket* sock;
	
protected:
	/*!	\brief The list of rooms the user is currently in.
	 *
	 *	The list of users this one is currently talking to.
	*/
	QList<GChatMap*> active;
	
	QList<GFileMap*> transfer;
	
	/*!	\brief Variable that stores weather or not the user is logged in.
	 *
	 *	Variable that stores weather or not the user is logged in.
	 *	The user cannot do anything until he logs in.
	*/
	bool loggedin;	
	
	/*! \brief General packet format: <packetType><data> all packets are terminated with a newline
	 *
	 ******packet definitions*****\n
	 *up	= up to server\n
	 *down	= down to client\n
	 *
	 *login\n
	 *up:	<login>`<username>`<password>\n
	 *down:	<servermessage>`<errorcode>`<error message>\n
	 *
	 *logout\n
	 *up:	<logout>\n
	 *down:	
	 *
	 *create account\n
	 *up:	<createaccount>`<username>`<password>\n
	 *down:	<servermessage>`<message #>`<message string>\n
	 *
	 *message\n
	 *up:	<message>`<username>`<the message>\n
	 *down:	\n
	 *
	 *
	 ******FRIEND*****\n
	 *get friend list\n
	 *up:	<friendlist>\n
	 *down:	<friendlist>`<name 0>`<name 1>`<name n>\n
	 *
	 *add to friend list\n
	 *up:	<friendadd>`<name>\n
	 *down: <friendadd>`<name>\n
	 *
	 *remove from friend list\n
	 *up:	<friendremove>`<name>\n
	 *down:	<friendremove>`<name>\n
	 *
	 *get status update from user on the friend list\n
	 *up:	\n
	 *down:	<friendstatus>`<name>`<status>\n
	 *
	 ******SQUELCH*****\n
	 *get squelch list\n
	 *up:	<squelchlist>\n
	 *down:	<squelchlist>`<name 0>`<name 1>`<name n>\n
	 *
	 *add to squelch list\n
	 *up:	<squelchadd>`<name>\n
	 *down:	<squelchadd>`<name>\n
	 *
	 *remove from squelch list\n
	 *up:	<squelchremove>`<name>\n
	 *down:	<squelchremove>`<name>\n
	 *
	 ******FILE TRANSFER*****\n
	 *initiate file transfer\n
	 *up:	<initiatetransfer>`<user>`<file name>`<file size>\n
	 *down: <initiatetransfer>`<user>`<file name>`<acknowledgement>\n
	 *
	 *initiate file transfer\n
	 *up:	<transferresponse>`<user>`<file name>`<acknowledgement>\n
	 *down: <transferresponse>`<user>`<file name>`<file size>\n
	 *
	 *send file chunk (sender end)\n
	 *up:	<filechunk>`<user>`<file name>`<chunk>\n
	 *down:	\n
	 *
	 *send file chunk (receiver end)\n
	 *up:	\n
	 *down:	<filechunk>`<user>`<file name>`<chunk>\n
	 *
	 *stop transfer\n
	 *up:	<transferstop>`<user>`<filename>\n
	 *down:	\n
	 *
	 ******CAPTCHA*****\n
	 *get captcha image\n
	 *up:	<captchaget>`<user>\n
	 *down:	<captchaget>`<user>`<data>\n
	 *
	 *verify captcha\n
	 *up:	<captchaverify>`<user>`<letter sequence>\n
	 *down:	<captchaverify>`<user>`<1/0 confirmation>\n
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	*/
	enum packetType {
		//for login and account creation
		accountlogin,
		accountcreate,
		accountlogout,
		
		sendmessage,
		servermessage,
		
		//for friend list interaction
		friendlist,
		friendadd,
		friendremove,
		friendmessage,
		friendstatus,
		
		//for file transfer
		transferstart,
		transferresponse,//11
		transferchunk,
		transferstop,
		
		//for squelch list interaction
		squelchlist,
		squelchadd,
		squelchremove,
		
		//captcha verification
		captchaget,
		captchaverify,
		
		//to get server turn around time
		ping//19
		
	};
	//	typedef unsigned char gheader;
	typedef int gheader;
	
	enum packetLength {
		//for login and account creation
		accountlogin_s = 3,
		accountcreate_s = 3,
		accountlogout_s = 1,
		
		sendmessage_s = 3,
		servermessage_s = 0,
		
		//for friend list interaction
		friendlist_s = 1,
		friendadd_s = 2,
		friendremove_s = 2,
		friendmessage_s = 2,
		friendstatus_s = 0,
		
		//for file transfer
		transferstart_s = 4,
		transferstop_s = 3,
		transferresponse_s = 4,
		transferchunk_s = 4,
		
		//for squelch list interaction
		squelchlist_s = 1,
		squelchadd_s = 2,
		squelchremove_s = 2,
		
		//captcha verification
		captchaget_s = 2,
		captchaverify_s = 3,
		
		//to get server turn around time
		ping_s = 1
	};
	
	bool validPacket(QStringList& args);
	
	void cleanUp();
	
	/*!	\brief Start of execution of every QThread.
	 *	
	 *	After this thread is created the creator must call start().
	 *	The start function calls run() then calls exec().
	 *	exec() is qt's built in run cycle that certain things need to work in a thread.
	*/	
	void run();
	
	/*! \brief Generate time information in the form of a QString.
	 *	
	 *	Generate time information in the form of a QString.
	*/
	inline QString timeStamp();
	
	/*!	\brief This function is used for debugging the incoming packets from a client.
	 *	
	 *	When a packet is received from a client the header is read into
	 *	a temporary variable. That temporary variable is passed to this
	 *	function as an arguement where the function returns the string
	 *	representation of that header so it may be printed to the console.
	 *	This function is used inside the readyRead() function.
	 *	
	 *	@see GUser::readyRead()
	 *	@param pack The header of any packet.
	 *	@return The QString representation of the header.
	 *	
	*/
	QString debugHeader(packetType pack);
	
	GResponse initializeMessage(QString user);
	void sendMessage(QString user, QString msg);
	void closeAllMessages();
	
	GResponse initializeFileTransfer(QString user, QString fn, int size);
	void sendChunk(QString user, QString chunk);
	void closeAllTransfers();
	void closeATransfer(QString user, QString fn);
public slots:
	void closeMessage(QString user);
	void receiveMessage(QString user, QString msg);
	
	void hostClosedTransfer(QString user, QString fn);
	void closeTransfer(QString user, QString fn);
	void receiveFileChunk(QString user, QString fn, QString chunk);
	
	/*!	\brief Other user responds to a file transfer request.
	*/
	void requestTransferResponse(QString user, QString fn, bool r);
	
	void requestFileTransfer(QString user, QString fn, int size);
	
	/*!	\brief Called when the socket connection is closed.
	 *	
	 *	When the user closes the connection or for whatever reason
	 *	the socket connection gets closed this function will be 
	 *	called by the socket. Currently it is setup so that when
	 *	the connection gets close this thread closes down.\n\n
	 *	Closing Process:\n
	 *	GLogin logs out this user.\n
	 *	GLogin sets the user's status to "offline".\n
	 *	The user's GFriendsList list is saved if it was changed while he was online.\n
	 *	The user's GSquelchList list is saved if it was changed while he was online.\n
	 *	The function quit() is called which closes down this GUser.\n
	 *	
	*/
	void disconnected();
	
	/*!	\brief Called whenever a packet is received from the client.
	 *	
	 *	When a packet is received from the client the socket calls this function so
	 *	the data may be processed. See the documentation for the enumeration
	 *	packetType for information on packet structure.
	 *	
	 *	@see packetType
	 *	
	*/
	void readyRead();
	
	/*!	\brief Called when an error is generated by the socket connection.
	 *	
	 *	This function is supposed to be used for handling of various errors
	 *	the socket connection could generate. However this function does not
	 *	have anything in it. There is no real need at the moment to handle
	 *	errors since no real errors occur after the connection is opened.
	 *	The only error that could occur would be one that pops up as a result
	 *	of the connection closing. In that case the disconnect() function
	 *	is called and the thread is just closed down normally.
	 *	
	 *	@param e The error to be handled.
	 *	
	*/
	void sockError(QAbstractSocket::SocketError e);
	
	/*!	\brief Called when a user on the GFriendsList updates their status.
	 *	
	 *	Status updates are done through the GLogin thread since it already keeps
	 *	an array of users.
	 *	
	 *	@see GLogin
	 *	@see GLoginMap
	 *	@param name The name of user on the GFriendsList who updated their status.
	 *	@param status The string representing the status of the user.
	 *	
	*/
	void receiveStatus(QString name, QString status);
	
signals:
	
	GResponse setupMessageChannel(GChatMap* ptr, QString user);
	
	GResponse setupFileTransferChannel(GFileMap* ptr, GUser* cur, QString user);
	void approveTheTransfer(QString user, QString fn, bool r);
	void stopFileTransfer(QString name, QString fn);
	
	void setAccountVerified(QString user);
	
	/*!	\brief Called to setup the GFriendsList so status updates may be sent.
	 *	
	 *	GFriendsList holds and array of GFriend which represents one friend.
	 *	After login the GUser thread iterates through the GFriendsList array
	 *	to setup all the connections so this user may receive status updates
	 *	from his friends. Status updates are actually mediated through the
	 *	GLogin thread. The linking process occurs by connectin each friend
	 *	in the GFriendsList to their respective object in the GLogin weather
	 *	or not they are online. Upon making the connection the last status of
	 *	friend is retreived from the GLogin array.
	 *	
	 *	@see Glogin
	 *	@see GLoginMap
	 *	@see GFriendsList
	 *	@param user A pointer to this GUser object.
	 *	@param ptr A pointer to a GFriendobject  in the the GFriendsList class.
	 *	@param index The last know index of the friend in the GLogin array.
	 *	@param name The name of the friend we will be emitting status updates to.
	 *	@return The actual index of the friend in the GLogin array.
	 *	
	*/
	int linkFriendList(GUser* user, GFriend* ptr, int index, QString name);
	
	/*!	\brief Called when this user wants to update his status to users on 
	 *	his GFriendsList.
	 *	
	 *	When this user wants to update his status to his freinds this function
	 *	is called. This signal is sent to every user in the GLogin array that
	 *	also exists in his GFriendsList. The Glogin array objects then send
	 *	the status update to GUser threads if the user is online.
	 *	
	 *	@param str The string representing this user's status.
	 *	
	*/
	void updateStatus(QString str);
	
	/*!	\brief Called when the user wants to login his account.
	 *	
	 *	This function is called inside readyRead() and sends a signal to the
	 *	GLogin instance when a user wants to login.
	 *	
	 *	@see GLogin
	 *	@param user A pointer to this GUser.
	 *	@param name The name of this GUser.
	 *	@param pass The password for this user's account.
	 *	@return eloginpassword if the login password does not match the database or
	 *	ealreadyloggedin if the user has already logged in his account or
	 *	eaccountdoesnotexist if the account does not exist in the database or
	 *	enone if the user successfully logged in.
	 *	
	*/
	GResponse loginAccount(GUser* user, QString name, QString pass);
	
	/*!	\brief Called when a user wants to logout his account.
	 *	
	 *	This function is called inside readyRead() and sends a signal to the
	 *	GLogin instance when this user wants to logout his account. It is
	 *	also used in disconnect() function if the connect closes for
	 *	whatever reason.
	 *	
	 *	@see Glogin
	 *	@param user A pointer to this GUser.
	 *	
	*/
	void logoutAccount(GUser* user);
	
	/*!	\brief Called when a user wants to create a new account.
	 *	
	 *	This function is called inside readyRead() and sends a signal to the
	 *	GLogin instance when the user wants to create a new account.
	 *	
	 *	@see GLogin
	 *	@param name The name of the user's account.
	 *	@param pass The password to authenticate the account for logging in.
	 *	@param email The email of the user's account, for account recovery
	 *	if he forgets his password.
	 *	@return echaracters if the name of the user contains reserved characters or
	 *	estringlimit if the name of the user is longer then the set limit or
	 *	eaccountalreadyexists if the account is already present in the database or
	 *	enone if the account was created successfully.
	 *	
	*/
	GResponse addAccount(QString name, QString pass, QString email);
	
	
};


#endif // GUSER_H
